// 使用 promise 模拟请求 + 3000ms后完成得到发射后结果
function createRequest(i) {
  return function () {
    return new Promise((resolve, reject) => {
      const start = Date.now();
      setTimeout(() => {
        if (Math.random() >= 0.05) {
          resolve(
            `第${i + 1}艘曲率飞船达到光速，成功逃离，用时${Date.now() - start}`
          );
        } else {
          reject(
            `第${i + 1}艘曲率飞船出现故障，无法达到光速，用时${
              Date.now() - start
            }`
          );
        }
      }, 3000 + i * 100);
    });
  };
}

class RequestControl {
  constructor({ max, el }) {
    this.max = max;
    this.requestQueue = [];
    this.el = document.querySelector(el);
    setTimeout(() => {
      this.requestQueue.length > 0 && this.run();
    });
    this.startTime = Date.now();
  }
  addRequest(request) {
    this.requestQueue.push(request);
  }
  async run() {
    // TODO：待补充代码
    //这是一个正在发射中的飞船队列
    let currentQueue = [];

    // for (let i = 0; i < 25; i++) {
    //   const request = createRequest(i);
    //   requestControl.addRequest(request);
    // }
    //在上面这个代码中其实就已经循环出了多个request请求对象，并且压入到了
    //requestQueue请求队列当中了   也就是说这个这个requestQueue队列中已经有了25个函数了
    //注意哈，为什么说这个requestQueue队列中存储的是25个函数呢

    //看下面这两句代码
    // const request = createRequest(i);
    // requestControl.addRequest(request);
    //第一句代码去调用createRequest(i)函数，那么你去上面这个函数查看一下他的具体实现
    //它的返回值是一个函数，在这个返回的函数中又返回了一个Promise对象
    //那我们就可以解读为执行createRequest(i)函数，我们拿到了一个执行函数，并将他存储到requestQueue队列
    //这里还要提醒一下我们只是为了读的时候方便把requestQueue称作队列，但是实际上他是一个数组，这个数组里面存储了25个函数

    //当我们这个请求发射的队列大于0的时候就去开始安排飞船到发射中队列里面去
    while (this.requestQueue.length > 0) {
      //这个if语句是为了控制同时有多个请求发出
      if (currentQueue.length < 10) {
        //首先我们去把requestQueue队列中的飞船拿到，并且将他从requestQueue移除
        //注意哈，此时我们拿到的是一个函数
        const request = this.requestQueue.shift();
        // 那么此时我们将这个函数（你就理解他是一个飞船），压入到发射的队列中去
        currentQueue.push(
          //这里是执行刚才拿到的函数，我们得到了一个Promise对象，
          // 再去分别去链式调用成功时候的回调函数和失败时候的回调函数
          request()
            .then((resolve) => this.render(resolve))
            .catch((reson) => this.render(reson))
        );
      }

      //如果说我们发射的队列满了的话怎么办
      if (currentQueue.length === 10) {
        //那我们就去等待
        // race()方法
        // 方法接收一个可迭代对象（如数组）作为参数，该对象的每个元素都是一个Promise实例
        // 或一个thenable对象。它返回一个新的Promise实例
        // 该Promise在任何一个输入Promise解析
        // （无论是fulfilled还是rejected）时就立即解析或拒绝，其结果或原因取自最先完成的那个Promise。
        await Promise.race(currentQueue);
        //当有Promise请求完成的时候我们就把当前队列最先完成的Promise请求移除
        currentQueue.shift();
      }
    }
  }
  render(context) {
    const childNode = document.createElement("li");
    childNode.innerText = context;
    this.el.appendChild(childNode);
  }
}

let requestControl = new RequestControl({ max: 10, el: "#app" });
for (let i = 0; i < 25; i++) {
  const request = createRequest(i);
  requestControl.addRequest(request);
}

module.exports = {
  requestControl,
};
